

package com.wechat.pay.java.service.transferbills;

import com.wechat.pay.java.core.Config;
import com.wechat.pay.java.core.cipher.PrivacyDecryptor;
import com.wechat.pay.java.core.cipher.PrivacyEncryptor;
import com.wechat.pay.java.core.http.*;
import com.wechat.pay.java.core.util.GsonUtil;
import com.wechat.pay.java.service.transferbills.model.*;

import java.util.Objects;

public class TransferBillsService {
    private final HttpClient httpClient;
    private final HostName hostName;
    private final PrivacyEncryptor encryptor;
    private final PrivacyDecryptor decryptor;

    private TransferBillsService(
            HttpClient httpClient,
            HostName hostName,
            PrivacyEncryptor encryptor,
            PrivacyDecryptor decryptor) {
        this.httpClient = Objects.requireNonNull(httpClient);
        this.hostName = hostName;
        this.encryptor = Objects.requireNonNull(encryptor);
        this.decryptor = Objects.requireNonNull(decryptor);
    }

    /**
     * TransferBillsService构造器
     */
    public static class Builder {

        private HttpClient httpClient;
        private HostName hostName;
        private PrivacyEncryptor encryptor;
        private PrivacyDecryptor decryptor;

        public Builder config(Config config) {
            this.httpClient = new DefaultHttpClientBuilder().config(config).build();
            this.encryptor = config.createEncryptor();
            this.decryptor = config.createDecryptor();
            return this;
        }

        public Builder hostName(HostName hostName) {
            this.hostName = hostName;
            return this;
        }

        public Builder httpClient(HttpClient httpClient) {
            this.httpClient = httpClient;
            return this;
        }

        public Builder encryptor(PrivacyEncryptor encryptor) {
            this.encryptor = encryptor;
            return this;
        }

        public Builder decryptor(PrivacyDecryptor decryptor) {
            this.decryptor = decryptor;
            return this;
        }

        public TransferBillsService build() {
            return new TransferBillsService(httpClient, hostName, encryptor, decryptor);
        }
    }

    /**
     * 发起商家转账
     * 商家转账用户确认模式下，用户申请收款时，商户可通过此接口申请创建转账单
     * 接口返回的HTTP状态码及错误码，仅代表本次请求的结果，不能代表订单状态。
     * 接口返回的HTTP状态码为200，且订单状态为ACCEPTED时，可认为发起商家转账成功。
     * 接口返回的HTTP状态码不为200时，请商户务必不要立即更换商户订单号重试。可根据错误码列表中的描述和接口返回的信息进行处理，并在查询原订单结果为失败或者联系客服确认情况后，再更换商户订单号进行重试。否则会有重复转账的资金风险。
     * 注：单个商户的接口频率限制为100次/s
     */
    public InitiateBillsTransferResponse initiateBillsTransfer(InitiateBillsTransferRequest request) {
        String requestPath = "https://api.mch.weixin.qq.com/v3/fund-app/mch-transfer/transfer-bills";
        // 加密敏感信息
        InitiateBillsTransferRequest realRequest = request.cloneWithCipher(encryptor::encrypt);
        if (this.hostName != null) {
            requestPath = requestPath.replaceFirst(HostName.API.getValue(), hostName.getValue());
        }
        HttpHeaders headers = new HttpHeaders();
        headers.addHeader(Constant.ACCEPT, MediaType.APPLICATION_JSON.getValue());
        headers.addHeader(Constant.CONTENT_TYPE, MediaType.APPLICATION_JSON.getValue());
        headers.addHeader(Constant.WECHAT_PAY_SERIAL, encryptor.getWechatpaySerial());
        HttpRequest httpRequest =
                new HttpRequest.Builder()
                        .httpMethod(HttpMethod.POST)
                        .url(requestPath)
                        .headers(headers)
                        .body(createRequestBody(realRequest))
                        .build();
        HttpResponse<InitiateBillsTransferResponse> httpResponse =
                httpClient.execute(httpRequest, InitiateBillsTransferResponse.class);
        return httpResponse.getServiceResponse();
    }


    /**
     * 撤销转账
     * 商户通过转账接口发起付款后，在用户确认收款之前可以通过该接口撤销付款。该接口返回成功仅表示撤销请求已受理，系统会异步处理退款等操作，以最终查询单据返回状态为准。
     */
    public CancelTransferResponse cancelTransfer(CancelTransferByOutNoRequest request) {
        String requestPath = "https://api.mch.weixin.qq.com/v3/fund-app/mch-transfer/transfer-bills/out-bill-no/" + request.getOutBillNo() + "/cancel";
        CancelTransferByOutNoRequest realRequest = request.cloneWithCipher(encryptor::encrypt);
        if (this.hostName != null) {
            requestPath = requestPath.replaceFirst(HostName.API.getValue(), hostName.getValue());
        }
        HttpHeaders headers = new HttpHeaders();
        headers.addHeader(Constant.ACCEPT, MediaType.APPLICATION_JSON.getValue());
        headers.addHeader(Constant.CONTENT_TYPE, MediaType.APPLICATION_JSON.getValue());
        headers.addHeader(Constant.WECHAT_PAY_SERIAL, encryptor.getWechatpaySerial());
        HttpRequest httpRequest =
                new HttpRequest.Builder()
                        .httpMethod(HttpMethod.POST)
                        .url(requestPath)
                        .headers(headers)
                        .body(createRequestBody(realRequest))
                        .build();
        HttpResponse<CancelTransferResponse> httpResponse =
                httpClient.execute(httpRequest, CancelTransferResponse.class);
        return httpResponse.getServiceResponse();
    }

    /**
     * 商户单号查询转账单
     * API只支持查询最近30天内的转账单，超过30天可以通过资金账单对账确认。
     * 转账单中涉及金额的字段单位为“分”。
     * 如果遇到新的错误码，请务必不要换单重试，请联系客服确认转账情况。如果有新的错误码，会更新到此API文档中。
     * 错误码描述字段message只供人工定位问题时做参考，系统实现时请不要依赖这个字段来做自动化处理。
     * 接口限频： 单个商户 100 QPS，如果超过频率限制，会报错 FREQUENCY_LIMITED，请降低频率请求。
     */
    public BillsTransferDetailEntity getTransferByOutNo(GetTransferByOutNoRequest request) {
        String requestPath = "https://api.mch.weixin.qq.com/v3/fund-app/mch-transfer/transfer-bills/out-bill-no/" + request.getOutBillNo();
        if (this.hostName != null) {
            requestPath = requestPath.replaceFirst(HostName.API.getValue(), hostName.getValue());
        }
        HttpHeaders headers = new HttpHeaders();
        headers.addHeader(Constant.ACCEPT, MediaType.APPLICATION_JSON.getValue());
        headers.addHeader(Constant.CONTENT_TYPE, MediaType.APPLICATION_JSON.getValue());
        headers.addHeader(Constant.WECHAT_PAY_SERIAL, encryptor.getWechatpaySerial());
        HttpRequest httpRequest =
                new HttpRequest.Builder()
                        .httpMethod(HttpMethod.GET)
                        .url(requestPath)
                        .headers(headers)
                        .build();
        HttpResponse<BillsTransferDetailEntity> httpResponse =
                httpClient.execute(httpRequest, BillsTransferDetailEntity.class);
        return httpResponse.getServiceResponse();
    }


    private RequestBody createRequestBody(Object request) {
        return (new JsonRequestBody.Builder()).body(GsonUtil.toJson(request)).build();
    }

}